home *** CD-ROM | disk | FTP | other *** search
/ CD Actual 1 / PC Actual CD 01.iso / share / dos / demos / planets / doc.lzh / PLANET.MS next >
Encoding:
Text File  |  1994-05-01  |  41.5 KB  |  1,093 lines

  1. .\"
  2. .\" Planet Animation
  3. .\" This is input data for the 'troff' text formatter (with ms macros)
  4. .\"
  5. .ds RH Planet Demo
  6. .^b HF 1
  7. .^b fh 0
  8. .TL
  9. The Definitive Guide to Bouncing Planets
  10. (a document with a modest title)
  11. .AU
  12. Simon Hern
  13. .AI
  14. 22 Harrington Drive
  15. Bedford MK41 8DB
  16. England
  17. .AB
  18. This document describes how to create a computer animation of a
  19. rotating planet using simple texture-mapping techniques. It should make
  20. an interesting programming project for someone with a basic knowledge of
  21. assembly language and computer graphics.
  22. .AE
  23. .LD
  24. August, 1992
  25. Revised December, 1992
  26. Revised April, 1994
  27. .DE
  28. .sp 3
  29. .NH 1
  30. Introduction
  31. .NH 2
  32. Planets, Past and Present
  33. .PP
  34. In the spring of 1990 a computer animation of a rotating planet lived
  35. out a brief, anonymous but happy existence. Years have passed.
  36. Now here's the sequel.
  37. .PP
  38. The greatest improvement of this program over the hacked-together-in-Basic
  39. original is the neatness and completeness of its design.
  40. More interesting improvements are:
  41. .RS
  42. .IP *
  43. Faster animation techniques.
  44. .IP *
  45. Choice of planet sizes and axes of rotation.
  46. .IP *
  47. Generation of semi-convincing planet surfaces.
  48. .IP *
  49. Illumination effects.
  50. .RE
  51. .PP
  52. The makers of the original 'Star Trek' would've died to use this animation
  53. as a special effect. That's how good it is.
  54. .NH 2
  55. Disclaimers and Apologies
  56. .PP
  57. There are a few things I ought to mention before going on.
  58. .PP
  59. Firstly, I am by no stretch of the imagination an expert in computer
  60. graphics, or for that matter computer programming of any description.
  61. Nearly all of what follows I figured out for myself during boring physics
  62. lessons (something I've always felt quite proud of). If there are any
  63. books that describe how to do this sort of thing, I've never seen them
  64. (though I would certainly like to).
  65. .PP
  66. Secondly, a work of stunning originality this isn't. When I started this
  67. project I had never seen or heard of anything like it. But that was
  68. probably more by chance than anything else since I've now had reports of
  69. people writing spinning planet animations (along very similar lines to what
  70. I'm describing here) as far back as the mid-eighties.
  71. .PP
  72. Finally, I must apologize for the naming of some of the concepts
  73. described here (Gush, Complexion, Riff, etc). They are not technical terms,
  74. merely the inventions of a warped mind (I was reading a lot of Clive
  75. Barker books at the time).
  76. .PP
  77. That's enough of that. Now on with the show.
  78. .NH 2
  79. Overview of the Program
  80. .PP
  81. The planet program divides up into three basic segments which I will cover
  82. (in long-winded detail) in turn.
  83. .PP
  84. The first part of the program creates a large array of data called the Gush
  85. which is required for the rapid animation of the planet. See Section
  86. 2.
  87. .PP
  88. The second part of the program generates a random surface for the planet
  89. and gives it colour. The resulting data array is called the Complexion.
  90. See Section 3.
  91. .PP
  92. The third part of the program uses these two data arrays to
  93. animate the planet in real time. For each frame of animation
  94. the entire planet is redrawn, rotated as required. See Section 5.
  95. .PP
  96. Sections 4 and 6 contain details on how the animation can be implemented
  97. in practice.
  98. .NH 2
  99. Planet Shapes and Sizes
  100. .PP
  101. The following constant values appear in various places throughout the
  102. program. They control certain aspects of the planet's appearance.
  103. .IP \fIRADIUS\fR 8
  104. This (integer) value is the radius in pixels of the planet as it
  105. appears on the screen.
  106. .IP \fIDIST\fR 8
  107. This is the apparent distance of the observer from the planet.
  108. It is measured in pixels from the planet's centre and would usually take
  109. a value in the order of several times the width of the screen. It must
  110. have a value greater than that of \fIRADIUS\fR. (The effect this value has
  111. is, to be honest, pretty minimal.)
  112. .IP \fIrho\fR 8
  113. This value (an integer) is the resolution of the planet's surface.
  114. It is the number of points of latitude (north to south) and half the number
  115. of points of longitude (around the equator) used when giving coordinates to
  116. the landscape.
  117. .IP \fIphi\fR 8
  118. This is the angle through which the axis of rotation of the planet is tilted
  119. backwards from the vertical (as seen by the observer).
  120. .IP \fItheta\fR 8
  121. This is the angle through which the axis of rotation of the planet is turned
  122. clockwise (as seen by the observer).
  123. .PP
  124. \fIphi\fR and \fItheta\fR do not need to be in any particular
  125. units since it is their sines and cosines that are required and the units
  126. conversion can take place when these are calculated.
  127. .PP
  128. These constants will be discussed in more detail as they are used.
  129. .NH 1
  130. Writing the Gush: Data for Spinning a Planet
  131. .NH 2
  132. From Screen to Surface
  133. .PP
  134. A lot of calculations need to be performed before drawing a planet.
  135. To speed up the animation procedure, the results of most of these
  136. calculations are worked out in advance and stored in a data array which I
  137. call the Gush.
  138. The Gush consists of data for transforming points of the planet as
  139. seen on the screen to points on the mercator map of the planet's surface.
  140. .PP
  141. Two stages are required to create the Gush. The first stage
  142. considers the Gush as a two-dimensional grid (the Square Gush), with
  143. each point corresponding to a pixel on the screen,
  144. and performs the lengthy floating-point calculations.
  145. In the second stage the data in the Gush is rearranged to make it more
  146. readily accessable to the animation routines. This second stage Gush is
  147. called the Line Gush.
  148. .NH 2
  149. Constants and the Gush
  150. .PP
  151. All of the constants described in Section 1.4 are referred to in the Gush
  152. calculations.
  153. .PP
  154. It is worth noting that the values of
  155. \fIDIST\fR, \fIphi\fR, \fItheta\fR and (possibly) \fIRADIUS\fR
  156. are needed only when creating the Gush and will probably not appear
  157. anywhere else in the program. As a result, Gushes created using different
  158. values for these constants can be used interchangeably.
  159. .NH 2
  160. The Square Gush
  161. .PP
  162. The Square Gush is an array of size \fI2*RADIUS\fR by \fI2*RADIUS\fR and is
  163. referenced by variables \fIxs\fR and \fIys\fR, both of which take values
  164. from \fI0\fR to \fI2*RADIUS-1\fR.
  165. Each entry in the array holds two values: \fIxm\fR and \fIym\fR,
  166. both non-negative integers of size depending on the choice of \fIrho\fR.
  167. .PP
  168. The array will be described using the notation \fIgush[ys][xs].xm_val\fR
  169. and \fIgush[ys][xs].ym_val\fR to mean the values of \fIxm\fR and \fIym\fR
  170. at the (\fIxs,ys\fR) position in the array.
  171. .PP
  172. The points of the Square Gush correspond directly to the pixels of the
  173. planet's image on the screen; the empty space around the planet is
  174. represented by null values in the array.
  175. .PP
  176. The values of \fIxm\fR and \fIym\fR for a point (\fIxs,ys\fR)
  177. are calculated in the following way.
  178. .PP
  179. First, define a new constant, \fIg\fR, as
  180. .CD
  181. .I
  182. g = DIST * RADIUS / sqrt( DIST*DIST + RADIUS*RADIUS )
  183. .R
  184. [\fIsqrt()\fR meaning 'square root']
  185. .PP
  186. Next, to make the calculations faster, work out the sines and cosines
  187. of \fIphi\fR and \fItheta\fR, calling them \fIsph\fR, \fIsth\fR, \fIcph\fR
  188. and \fIcth\fR.
  189. .PP
  190. For each point (\fIxs,ys\fR) evaluate the following temporary values:
  191. .CD
  192. .I
  193. xg = xs - RADIUS + 0.5
  194. yg = ys - RADIUS + 0.5
  195.  
  196. j = sqrt( xg*xg + yg*yg )
  197. .PP
  198. If \fIj\fR is greater than \fIRADIUS\fR then (\fIxs,ys\fR) is not a point
  199. in the planet's image and should be ignored; set \fIxm = ym = -1\fR (or some
  200. other null value). Otherwise,
  201. .CD
  202. .I
  203. h = sqrt( DIST*DIST * ( g*g - j*j ) + g*g * j*j )
  204. k = ( DIST*DIST - h ) / ( DIST*DIST + j*j )
  205.  
  206. xp = k * xg
  207. yp = k * yg
  208. zp = sqrt( g*g - xp*xp - yp*yp )
  209. .R
  210. .PP
  211. (The vector \fI(xp,yp,zp)\fR, called \fIP\fR, can be used to
  212. calculate illumination effects. See Section 6.2.)
  213. .CD
  214. .I
  215. xt = xp*cth - yp*sth*cph + zp*sth*sph
  216. yt = xp*sth + yp*cth*cph - zp*cth*sph
  217. zt = yp*sph + zp*cph
  218.  
  219. w = sqrt( g*g - yt*yt )
  220. alpha = acos( yt / g )
  221. beta = acos( - xt / w )
  222. .R
  223. [\fIacos()\fR meaning 'inverse cosine']
  224. .PP
  225. All of these calculations use floating-point arithmetic. \fIxm\fR
  226. and \fIym\fR, on the other hand,
  227. are integers, and when converting from floating-point to integer values
  228. in the next set of calculations
  229. the 'integer part' of the number (the largest integer not greater than
  230. that number) should be taken.
  231. .CD
  232. .I
  233. xm = (int) beta * rho / pi
  234. ym = (int) alpha * rho / pi
  235.  
  236. .R
  237. [\fIpi\fR is (predictably enough) the constant 3.14159265(etc).
  238. All the trigonometric calculations here operate in radians.]
  239.  
  240. .I
  241. if xm = rho then let xm = rho - 1
  242. if ym = rho then let ym = rho - 1
  243. if zt < 0 then let xm = 2*rho - 1 - xm
  244. .R
  245. .PP
  246. \fIxm\fR takes values from \fI0\fR to
  247. \fI2*rho-1\fR and \fIym\fR takes values from \fI0\fR to \fIrho-1\fR.
  248. These are the coordinates of a point on the mercator map of the planet's
  249. surface.
  250. .PP
  251. The values of \fIxm\fR and \fIym\fR can now be stored in the Gush array.
  252. .CD
  253. .I
  254. gush[ys][xs].xm_val = xm
  255. gush[ys][xs].ym_val = ym
  256. .R
  257. .NH 2
  258. The Line Gush
  259. .PP
  260. The Line Gush is all of the important data from the Square Gush
  261. rearranged in such a way as to make the final animation as
  262. fast as possible (this being where the name 'Gush' comes in).
  263. .PP
  264. The Line Gush takes the form of a one-dimensional array, a string of
  265. values in the order they are used to draw the planet.
  266. .PP
  267. The actual format of the Line Gush is determined by the procedure
  268. used to display the planet. Here is an example Line Gush format. For more
  269. details see Sections 4 and 5.
  270. .PP
  271. The first entry in the Line Gush is the number of horizontal lines
  272. needed to draw the planet, i.e., the planet's height in pixels.
  273. It is equal to \fI2*RADIUS\fR.
  274. .PP
  275. Since the planet is circular, these horizontal lines will be
  276. of different lengths.
  277. The Square Gush treats all the lines as being of the same length and
  278. fills in the gaps with 'null' entries.
  279. These nulls are missed out when putting together the Line Gush.
  280. .PP
  281. Now, for each horizontal line of the planet (corresponding to each value
  282. taken by \fIys\fR), the following values are stored in the Line Gush:
  283. .IP (1) 5
  284. The relative screen position of the start of the line.
  285. Since the lines are all of different lengths, the Gush must provide
  286. information on how to arrange them to give a circular shape. This
  287. information will probably take the form of an amount
  288. to be added to the screen address of the last point plotted
  289. on the previous line to give the address of the first point to plot
  290. on this new line.
  291. .IP (2) 5
  292. A function of the length of the line; this value determines
  293. the number of points to plot. (For speed of animation,
  294. it may be other than simply the number of points on the line.)
  295. .IP (3) 5
  296. For each point on the line, that point's \fIxm\fR and \fIym\fR values
  297. (possibly combined into a single value).
  298. .PP
  299. It takes a considerable amount of time to create a full (Line) Gush
  300. so it is advisable to save the completed data to disk rather than to create
  301. new data each time.
  302. .PP
  303. There is no simple formula for the size of the Line Gush,
  304. but in the above example it will be smaller than the Square Gush which
  305. consists of \fI2*4*RADIUS*RADIUS\fR bytes (or words, depending on how
  306. \fIxm\fR and \fIym\fR are stored).
  307. .PP
  308. An alternative approach is to create a Compiled Gush, an executable mixture
  309. of data and machine code which allows for very fast animation, but which
  310. takes up a large amount of memory.
  311. .NH 1
  312. Third Day Song
  313. .NH 2
  314. Making Planets
  315. .PP
  316. This part of the program creates a random, vaguely realistic-looking
  317. planet surface which is something like fractally generated. (In my brief
  318. search around the library I couldn't find any (comprehensible)
  319. information about generating realistic spherical planet surfaces.)
  320. .PP
  321. The method used is as follows. The planet's surface (a sphere) is
  322. cut around a random equator into two hemispheres. One of the
  323. hemispheres is selected and the altitudes of all points on it increased or
  324. decreased relative to the other hemisphere. This is repeated many
  325. times with the relative changes in altitude gradually reducing in
  326. significance.
  327. .PP
  328. This process acts on an array of altitude values called the Skin, which
  329. describes in a two dimensional mercator map the spherical planet surface.
  330. When the Skin is complete it is transformed into an array of colour
  331. values called the Complexion. It is this which is used in the animation.
  332. .NH 2
  333. The Skin
  334. .PP
  335. The Skin is a two-dimensional array of size \fIrho\fR by \fI2*rho\fR
  336. (this being the definition of the value \fIrho\fR), and each entry in it
  337. represents the altitude of a point on the surface of the planet.
  338. The Skin will be referred to by \fIskin[y][x]\fR
  339. where \fIy\fR is the colatitude of a
  340. point on the surface and takes values from \fI0\fR to \fIrho-1\fR, and
  341. \fIx\fR is the longitude of a point on the surface and takes values
  342. from \fI0\fR to \fI2*rho-1\fR. Note that the skin wraps around in the
  343. \fIx\fR sense (so \fIx=2*rho\fR is equivalent to \fIx=0\fR), but not
  344. in the \fIy\fR sense; \fIy=0\fR marks the north pole, \fIy=rho-1\fR marks
  345. the south pole.
  346. .PP
  347. The elements in this array could be floating-point numbers, but, to
  348. speed up calculations, it is probably best to use integers. All of the
  349. altitude values are initially set to zero, or some other appropriate
  350. base value.
  351. .NH 2
  352. The Riffs
  353. .PP
  354. To generate the planet's surface, the Skin must be treated as if it is
  355. spherical when it is, in fact, flat. This is achieved with the help
  356. of a set of data arrays, called the Riffs, which describe the shapes of
  357. the possible fractures that can be made in the surface. The Riffs are
  358. unchanging; the random planet surfaces (for fixed \fIrho\fR) are all
  359. created using the same set of Riffs.
  360. .PP
  361. There are \fIrho/2\fR Riffs, numbered from \fI0\fR to \fIrho/2-1\fR, each
  362. consisting of \fIrho/2\fR (again \fI0\fR to \fIrho/2-1\fR) integer values
  363. between \fI0\fR and \fIrho/2\fR inclusive.
  364. Let the Riffs be represented by \fIriff[A][t]\fR where \fIA\fR is the
  365. Riff number and \fIt\fR is the position within that Riff.
  366. .PP
  367. The Riffs are generated in the following way with
  368. \fIA\fR and \fIt\fR both taking values \fI0\fR to \fIrho/2-1\fR:
  369. .CD
  370. .I
  371. omega = ( A + 0.5 ) * pi / rho
  372. sigma = ( t + 0.5 ) * pi / rho
  373. gamma = atan( tan(omega) * cos(sigma) )
  374. riff[A][t] = rho/2 - round( gamma * rho / pi )
  375. .R
  376.  
  377. [\fIround()\fR means 'to the nearest integer'.
  378. \fIatan()\fR means 'inverse tangent'.]
  379. .PP
  380. It can take a little time to create all the Riffs, so it is not a bad idea
  381. to save them to disk for future use.
  382. .NH 2
  383. The Song
  384. .PP
  385. This section describes the actual routine that generates the
  386. planet's surface. It is fairly complicated.
  387. .PP
  388. For this part of the program the Skin (with all entries initially set to
  389. zero), the Riffs, and the constant \fIrho\fR are required.
  390. A couple of new constants also need to be defined.
  391. .IP \fIFULL_BLAST\fR 12
  392. This value determines the size of the fractures made in the
  393. surface; it is the actual size of the first fracture made.
  394. .IP \fIPOWER\fR 12
  395. This constant controls the style of formation of the surface. A value of
  396. 1 results in mostly straight coastlines whilst a higher value gives
  397. more jagged shapes. Try a value of 3, 4 or 5.
  398. .PP
  399. The number of 'strikes' used to make the surface (and hence the time taken)
  400. is equal to \fIFULL_BLAST\fR to the power of \fIPOWER\fR. Something in
  401. the order of several thousand strikes I find are needed to generate a
  402. convincing surface.
  403. .PP
  404. The procedure that follows is written in an vague approximation of the
  405. C language.
  406. .ID
  407. .I
  408. FULL_BLAST = 20.0
  409. POWER = 3.0
  410.  
  411. int skin[rho][2*rho];  /* Using integer altitude values */
  412. int riff[rho/2][rho/2];  /* Riff data already generated */
  413.  
  414. int strike;  /* Number of fractures to make */
  415. int displac = 0;  /* Move everything upwards; rebalance later */
  416.  
  417. int blast;  /* Depth of next fracture */
  418. int rf_num;  /* Riff to use on this fracture */
  419. int r1;  /* Preliminary random choice of riff */
  420. int half;  /* Random choice of which half of surface to hit */
  421. int dirn;  /* Random choice of whether to blast up or down */
  422. int x_start;  /* Position around equator to start at */
  423. float r2, dis;  /* Weight riff probabilities */
  424.  
  425. int x, y;  /* Every program needs some x and y coordinates */
  426. int x1, x2, x3, x4;  /* A few spare x coordinates */
  427.  
  428.  
  429. /* 'strike' is initially FULL_BLAST to the power of POWER */
  430. /* We repeat until strike (decremented by one each time) is zero */
  431. for ( strike = pow( FULL_BLAST, POWER ) ; strike > 0 ; strike-- ) {
  432.  
  433.     /* pow(A,B) means A to the power of B (A^B or A**B) */
  434.     /* Watch out for conversions between 'int' and 'float' here */
  435.     blast = FULL_BLAST / pow( strike, 1.0/POWER );
  436.  
  437.     /* Choose random values for next fracture */
  438.     r1 = random(rho/2);  /* int from 0 to rho/2-1 (inclusive) */
  439.     x_start = random(2*rho);  /* 0 to 2*rho-1 (inclusive) */
  440.     half = random(2);  /* 0 or 1 */
  441.     dirn = random(2);  /* 0 or 1 */
  442.     r2 = ((float)rand())/(float)RAND_MAX;  /* between 0 and 1 */
  443.  
  444.     /* Balance choice of riffs evenly over the surface */
  445.     /* (Avoids most fractures being along the equator) */
  446.     dis = sin( pi * (r1+0.5) / rho );  /* All in floating-point */
  447.     if ( r2 <= dis*dis ) rf_num = r1;
  448.     else rf_num = rho/2 - 1 - r1;
  449.  
  450.     /* We only displace in the northern half of the planet */
  451.     /* then make up for it at the end using 'displac' */
  452.     if ( dirn == 0 ) blast = -blast;
  453.     if ( half == 0 ) displac = displac - blast;
  454.  
  455.     /* This loop would be best converted to assembly language */
  456.     /* The loop goes from x equal to 0 to x equal to rho/2-1 */
  457.     /* Note: A%B means the remainder of A divided by B (A mod B) */
  458.     /*       A+=B means A=A+B */
  459.     for ( x = 0 ; x < rho/2 ; x++ ) {
  460.         x1 = ( x_start + x ) % (2*rho);
  461.         x2 = ( x_start + 2*rho-1 - x ) % (2*rho);
  462.         for ( y = 0 ; y < riff[rf_num][x] ; y++ ) {
  463.         /* If riff[rf_num][x] is zero this loop is not executed */
  464.             skin[y][x1] += blast;
  465.             skin[y][x2] += blast;
  466.         }
  467.         x3 = ( x_start + rho + x ) % (2*rho);
  468.         x4 = ( x_start + rho-1 - x ) % (2*rho);
  469.         for ( y = 0 ; y < rho-1 - riff[rf_num][x] ; y++ ) {
  470.         /* If riff[rf_num][x] is rho-1 this loop is not executed */
  471.             skin[y][x3] += blast;
  472.             skin[y][x4] += blast;
  473.         }
  474.     }
  475. }
  476.  
  477. /* Finally, shift all the altitudes to rebalance the zero level */
  478. /* (This is not strictly necessary since altitudes are relative) */
  479. for( y = 0 ; y < rho ; y++ ) for( x = 0 ; x < 2*rho ; x++ )
  480.     skin[y][x]+=displac;
  481. .R
  482. .DE
  483. .PP
  484. This routine assumes that the Skin is made up of integer values
  485. and so uses mostly integer variables. The advantages of using
  486. ints rather than floats are that they use less
  487. memory, they operate faster, and they allow the program to be
  488. more easily converted into assembly language.
  489. .PP
  490. Bear in mind that integer variables can 'overflow' if they are required
  491. to store very large numbers. If the program begins to produce unexpected
  492. results, this could be the cause.
  493. .NH 2
  494. The Complexion
  495. .PP
  496. The Complexion is an array of the same size and shape as the Skin and
  497. is referred to by \fIxion[y][x]\fR. Each entry in the Complexion is a colour
  498. value and the array as a whole forms a coloured-in map of the planet's
  499. surface.
  500. .PP
  501. The Complexion is derived from the Skin by assigning colours based on
  502. the altitude at each point. A simple way to do this is as follows.
  503. .PP
  504. Choose a set of \fIK\fR colours such that colour \fI1\fR represents the
  505. lowest altitude and colour \fIK\fR represents the highest altitude.
  506. Look at the Skin to find the maximum and minimum altitude values
  507. and divide up the range of altitudes into \fIK\fR equal-sized groups, each
  508. corresponding to a colour. Then, if the maximum and minimum altitudes
  509. are the values \fImax\fR and \fImin\fR,
  510. .CD
  511. .I
  512. xion[y][x] = int{ ( skin[y][x] - min ) * K / (max-min+1) } + 1
  513. .R
  514. .PP
  515. For a very simple planet design, colour any point with
  516. an altitude of \fI(max+min)/2\fR or less sea-blue, and any other point
  517. grass-green.
  518. .NH 1
  519. Notes on Implementation
  520. .NH 2
  521. Attachment to Hardware
  522. .PP
  523. The details of writing this program depend greatly on the hardware being
  524. used: the CPU instruction set, the available memory, the graphics
  525. specifications and the computer's speed, among other things.
  526. .PP
  527. In this section I'll sketch out how I envisage this program being
  528. implemented to make the most of the hardware available.
  529. .NH 2
  530. Choice of Screen Mode
  531. .PP
  532. The way in which the screen memory of the computer is organised
  533. determines to a large extent the structure of the animation routine.
  534. .PP
  535. The simplest way to write to the screen would be to use a function
  536. of the type \fIplot(x_pos, y_pos, colour)\fR, but this would give
  537. painfully slow animation. It is usually much better to write directly to
  538. screen memory.
  539. .PP
  540. The nicest screen mode for this sort of animation would have each pixel
  541. controlled by one byte of screen memory (suggesting a mode with 256 colours)
  542. and would have consecutive pixels along a horizontal line corresponding to
  543. consecutive bytes in memory, with the horizontal lines arranged in
  544. order top to bottom. With other kinds of screen arrangement animation
  545. routines become a little more tricky to put together.
  546. .PP
  547. Some screen modes use pixels which aren't exact squares, and this
  548. results in planets that are elliptical instead of circular.
  549. To compensate for this the \fI(xs,ys)\fR coordinates used in the Square
  550. Gush have to be rescaled (the result being, in effect, a Rectangular Gush).
  551. .PP
  552. The choice of screen resolution is a balancing of two factors: for
  553. planets of a fixed size, a low-resolution display gives a less detailed
  554. image whilst a high-resolution display means slower animation.
  555. A resolution of about 300 pixels by 200 pixels with a planet radius of
  556. 50 pixels (which is to say \fIRADIUS=50\fR) is a reasonable place to start.
  557. .NH 2
  558. Gush Structure
  559. .PP
  560. The amount of memory needed for the Gush depends on a lot of factors;
  561. anything from two to ten bytes may be needed for each point to be plotted,
  562. the number of which will be around \fIPI*RADUIS*RADIUS\fR.
  563. A straightforward Line Gush for a planet with \fIRADIUS=50\fR and
  564. \fIrho=128\fR may take up around 20k. A Compiled Gush for the same
  565. planet may take up closer to 60k.
  566. .PP
  567. The value \fIDIST\fR has only a very slight effect on the appearance of the
  568. planet. I usually give it a value somewhere in the thousands. It can
  569. alternatively be omitted altogether: change the Gush calculations so that
  570. \fIg=RADIUS\fR and \fIk=1\fR. This is equivalent to making \fIDIST\fR
  571. infinitely large (and has the advantage of speeding up the Gush
  572. calculations.)
  573. .PP
  574. As default values for \fIphi\fR and \fItheta\fR, try 45 and 30
  575. degrees respectively.
  576. .NH 2
  577. Complexion Structure
  578. .PP
  579. The resolution of the planet's surface, \fIrho\fR, should be given a value
  580. in compromise of two factors: if \fIrho\fR is not large enough the planet's
  581. surface will be too blocky, but as \fIrho\fR becomes larger more memory is
  582. required for the Complexion and more time is needed to generate a planet
  583. surface. Also, very large values of \fIrho\fR give no obvious
  584. improvement to the appearance of the planet.
  585. .PP
  586. In addition, the value of \fIrho\fR determines the number of different
  587. frames seen when the planet is animated; the number of positions of
  588. rotation the planet can be viewed in is equal to \fI2*rho\fR.
  589. .PP
  590. In general, \fIrho\fR can be given any value, but a value of \fI128\fR
  591. may give certain advantages. Since the array \fIxion[y][x]\fR uses
  592. values of \fIx\fR between \fI0\fR and \fI2*rho-1\fR and values of \fIy\fR
  593. between \fI0\fR and \fIrho-1\fR, \fIrho=128\fR means that both \fIx\fR and
  594. \fIy\fR values are byte-sized.
  595. Also, the offset into the \fIxion\fR array is \fI2*rho*y+x\fR which
  596. becomes \fI256*y+x\fR, so x and y are now the low order and high order
  597. bytes of a CPU-friendly 16-bit word.
  598. .PP
  599. Each point in the Complexion is a colour value; if no more than 256
  600. colours are used, then each entry in the array will take up one byte.
  601. The total amount of memory used will then be \fI2*rho*rho\fR bytes.
  602. .NH 1
  603. Magrathea in Scattered Jumps: Plotting a Planet
  604. .NH 2
  605. Animation Routines
  606. .PP
  607. And so, at last, we arrive at the heart of the program: the animation routine.
  608. .PP
  609. What I'm going to do in this section is describe a progression of methods
  610. for displaying planets, starting with simple routines to help explain the
  611. process and then moving on to more effective methods.
  612. .PP
  613. Since the speed of the display routine is critical it will almost certainly
  614. need to be written in assembly language. With this in mind, the descriptions
  615. of the routines are in a fairly low-level pseudo-code.
  616. .PP
  617. Four bits of data are needed by the routines when displaying a planet:
  618. .IP \fIxion\fR 6
  619. The routine must have access to some Complexion data; the name \fIxion\fR
  620. will be used to mean, depending upon the routine, either the entire
  621. array \fIxion[y][x]\fR or the address in memory of (i.e., a pointer to)
  622. the first byte in the array.
  623. .IP \fIgush\fR 6
  624. The routine needs Gush data of some description.
  625. Like \fIxion\fR, \fIgush\fR will refer to either an array or a memory
  626. address as the routine requires.
  627. .IP \fIscr\fR 6
  628. This value determines where on the screen the planet is displayed.
  629. It could be either the x and y coordinates of a point on the screen
  630. (referred to as \fIscr.x\fR and \fIscr.y\fR),
  631. or the address of a point in screen memory.
  632. The point in question will be the top-left 'corner' of the planet.
  633. .IP \fIrot\fR 6
  634. This is the state of rotation of the planet, the degree to which it appears
  635. rotated about its axis when drawn. It is a value between \fI0\fR and
  636. \fI2*rho-1\fR with wrap-around meaning that \fIrot=2*rho\fR is equivalent
  637. to \fIrot=0\fR.
  638. .NH 2
  639. Using the Square Gush
  640. .PP
  641. This is the simplest of the display methods; it uses the Square Gush
  642. as input, treating it as an array, and plots points to the screen using
  643. a \fIplot\fR function.
  644. This method could be implemented in a high-level language to
  645. test how well the program works before going to the trouble of writing
  646. a faster assembly language routine.
  647. .PP
  648. In addition to the parameters which are passed to the routine (see 5.1),
  649. five local variables are used here: \fIys\fR and \fIxs\fR are the
  650. coordinates of a point in the Gush and on the screen;
  651. \fIcol\fR is the colour of the current point; \fIxm\fR and \fIym\fR are
  652. values taken from the Gush data which refer to a point in the Complexion.
  653. .PP
  654. Remember that the Square Gush includes a number of 'null'
  655. entries for which both \fIxm_val\fR and \fIym_val\fR are taken here to
  656. be equal to \fI-1\fR.
  657. .ID
  658. .I
  659. ys = 0
  660. L1:
  661. xs = 0
  662. L2:
  663.  
  664. xm = gush[ys][xs].xm_val
  665. ym = gush[ys][xs].ym_val
  666. if ( xm = -1 ) and ( ym = -1 ) jump to L3
  667. xm = ( xm + rot ) % (2*rho)
  668. col = xion[ym][xm]
  669. plot( (scr.x + xs), (scr.y + ys), col )
  670.  
  671. L3:
  672. xs += 1
  673. if ( xs != 2*RADIUS ) jump to L2
  674. ys += 1
  675. if ( ys != 2*RADIUS ) jump to L1
  676. .R
  677. .DE
  678. .PP
  679. It should be obvious roughly what this routine does. For each point in
  680. an area of the screen, \fIxm\fR and \fIym\fR values are taken from the
  681. corresponding point in the Square Gush.
  682. If the point is valid (i.e., part of the planet) then a
  683. colour value is derived from the Complexion and the point is plotted.
  684. .PP
  685. The value \fIrot\fR is added to \fIxm\fR, the result being trimmed so that it
  686. remains between \fI0\fR and \fI2*rho-1\fR. In this way, the planet's
  687. surface is effectively made to rotate around the planet. (This is the only
  688. time that \fIrot\fR is used in the program.)
  689. .PP
  690. (Incidentally, 'A != B' means 'A not equal to B' and 'A % B'
  691. means 'remainder of A divided by B'.)
  692. .NH 2
  693. Using the Line Gush
  694. .PP
  695. Because the Line Gush does not include the 'null' entries of the Square Gush,
  696. a routine using it can run faster. The Line Gush is a sequence of values
  697. in memory which are accessed in order. The notation \fIx=[gush++]\fR will
  698. be used to mean 'load the next Gush value into \fIx\fR' - the variable/register
  699. \fIgush\fR holds the address of the next value to be used; this value is
  700. loaded into \fIx\fR and \fIgush\fR is incremented by an appropriate amount.
  701. .PP
  702. The format of the Line Gush is defined by the structure of the routine.
  703. In this example, the first value is the total number of lines to draw and
  704. is followed by the appropriate data for each line consisting of:
  705. .IP (1) 5
  706. An amount to add to or subtract from \fIscr.x\fR, the x-coordinate of the next
  707. point to be plotted, so that this line is drawn to fit the circular shape
  708. of the planet.
  709. .IP (2) 5
  710. The number of points to be plotted on this line.
  711. .IP (3) 5
  712. For each point on this line, a value for \fIxm\fR followed by a value
  713. for \fIym\fR.
  714. .PP
  715. The local variables used in this routine are the same as those used in
  716. Section 5.2 except that \fIxs\fR and \fIys\fR are renamed \fIxcount\fR
  717. and \fIycount\fR to reflect their slightly different application.
  718. .ID
  719. .I
  720. ycount = [gush++]
  721. L1:
  722. scr.x += [gush++]
  723. xcount = [gush++]
  724. L2:
  725.  
  726. xm = [gush++]
  727. ym = [gush++]
  728. xm = ( xm + rot ) % (2*rho)
  729. col = xion[ym][xm]
  730. plot( scr.x, scr.y, col )
  731. scr.x += 1
  732.  
  733. xcount -= 1
  734. if ( xcount != 0 ) jump to L2
  735.  
  736. scr.y += 1
  737. ycount -= 1
  738. if ( ycount != 0 ) jump to L1
  739. .R
  740. .DE
  741. .PP
  742. Note that the value of \fIRADIUS\fR is no longer needed here - all the
  743. required data is stored in the Gush.
  744. .NH 2
  745. Screen Memory Addresses
  746. .PP
  747. The most obvious way of increasing the speed of the previous routine
  748. is to avoid using the \fIplot\fR function and to instead write directly
  749. to the screen memory.
  750. .PP
  751. Section 4.2 lists the assumptions I am making about the layout
  752. of the screen memory. The variable/register named \fIscr\fR will now
  753. hold the memory address of the next point of the screen to be altered.
  754. The notation \fI[scr]=x\fR will be used to mean that the byte or word
  755. at screen memory address \fIscr\fR is to be loaded with the value \fIx\fR.
  756. .PP
  757. Apart from \fIscr\fR, the only other difference between this routine and
  758. the last one is in the Line Gush: the value which before
  759. was added to \fIscr.x\fR is now added to \fIscr\fR. This new value is
  760. the difference between the address of the last point on the previous
  761. line of the planet and the address of the first point on the new line of
  762. the planet.
  763. .ID
  764. .I
  765. ycount = [gush++]
  766. L1:
  767. scr += [gush++]
  768. xcount = [gush++]
  769. L2:
  770.  
  771. xm = [gush++]
  772. ym = [gush++]
  773. xm = ( xm + rot ) % (2*rho)
  774. col = xion[ym][xm]
  775. [scr] = col
  776. scr += 1
  777.  
  778. xcount -= 1
  779. if ( xcount != 0 ) jump to L2
  780. ycount -= 1
  781. if ( ycount != 0 ) jump to L1
  782. .R
  783. .DE
  784. .NH 2
  785. Games with Xion
  786. .PP
  787. All of the routines so far have treated \fIxion\fR as an array; in a
  788. low-level language the address in memory of a value is needed instead
  789. of its position in the array, so we replace the line
  790. .CD
  791. .I
  792. col = xion[ym][xm]
  793. .R
  794. .DE
  795. .LP
  796. with the line
  797. .CD
  798. .I
  799. col = [ xion + 2*rho*ym + xm ]
  800. .R
  801. .DE
  802. .PP
  803. (This assumes that one byte is used for each of the values in the
  804. Complexion.)
  805. .NH 3
  806. .PP
  807. The use of \fIxm\fR and \fIym\fR to point to values in the Complexion
  808. is somewhat inefficient. One fairly simple way of speeding the process
  809. up would be to store the value of \fI2*rho*ym\fR or even
  810. \fIxion+2*rho*ym\fR in the Gush instead of just \fIym\fR.
  811. The modulus ('%') calculation can also be made faster by
  812. choosing a value for \fIrho\fR that is a power of two; then the modulus
  813. operation can be replaced by a logical AND operation which most
  814. microprocessors can perform much faster. If this is done then the
  815. central loop of the routine will look something like:
  816. .ID
  817. .I
  818. xm = [gush++]
  819. ym2 = [gush++]
  820. xm = ( xm + rot ) AND (2*rho-1)
  821. col = [ ym2 + xm ]
  822. [scr] = col
  823. scr += 1
  824. .R
  825. .DE
  826. .NH 3
  827. .PP
  828. An alternative approach can be used on CPUs which allow their 16- or
  829. 32-bit registers to be split into component 8-bit registers. Suppose one
  830. complete register is called \fIyyxx\fR and that its lowest 8 bits
  831. can be operated upon separately as the register \fIxx\fR.
  832. Since \fIxx\fR is only 8 bits long, the equivalent of an \fIAND 255\fR
  833. operation is automatically performed on it whenever it is used.
  834. Thus if \fIrho\fR is set equal to 128 and the Gush holds the combined value
  835. \fI2*rho*ym+xm\fR instead of both \fIxm\fR and \fIym\fR, then the core
  836. of the routine is simply:
  837. .ID
  838. .I
  839. yyxx = [gush++]
  840. xx += rot
  841. col = [ xion + yyxx ]
  842. [scr] = col
  843. scr += 1
  844. .R
  845. .DE
  846. .NH 3
  847. .PP
  848. There is yet a third way to simplify the routine, though it requires
  849. twice as much memory for the Complexion data.
  850. An expanded Complexion is created in which each line of the map appears
  851. twice: Line 1 is followed in memory by another
  852. copy of Line 1 before getting to Line 2, such that \fI[xion]\fR is the
  853. same as \fI[xion+2*rho]\fR, \fI[xion+1]\fR is the same as
  854. \fI[xion+2*rho+1]\fR, and so on up until \fI[xion+4*rho]\fR which is the
  855. start of the second line.
  856. .PP
  857. An expanded Complexion removes the need for the modulus ('%')
  858. calculation when \fIrot\fR is added on to \fIxm\fR.
  859. .PP
  860. Instead of the two values \fIxm\fR and \fIym\fR being used for each
  861. point in the Line Gush, the single combined value \fIxion+4*rho*ym+xm\fR
  862. (or possibly just \fI4*rho*ym+xm\fR) is used. The single
  863. variable/register \fIymxm\fR replaces both \fIxm\fR and \fIym\fR
  864. and the core of the routine now becomes:
  865. .ID
  866. .I
  867. ymxm = [gush++]
  868. col = [ ymxm + rot ]    ( \fRor\fI [ xion + ymxm + rot ]  )
  869. [scr] = col
  870. scr += 1
  871. .R
  872. .DE
  873. .PP
  874. (Note that \fI4*rho\fR now replaces \fI2*rho\fR as the width of the
  875. Complexion array.)
  876. .NH 2
  877. Scattered Jumps
  878. .PP
  879. There is another aspect of the display routine which can still be made
  880. more efficient: the looping mechanism. For every point of the planet that is
  881. plotted, additional instructions must be executed to loop back to plot the
  882. next point. These time wasting loop instructions can be missed out by
  883. repeating the plotting code in memory once for each of the points to be
  884. plotted.
  885. .PP
  886. The problem with this is that the number of points to be plotted on each
  887. line varies. The solution is to miss out some of the plot routines
  888. by jumping past them - this needs an 'indirect' jump instruction, the
  889. address to jump to being stored in a register or in memory.
  890. .PP
  891. The following code shows how this method could be implemented into a
  892. display routine. It is based around an expanded Complexion, as described
  893. in the previous sub-section. The value in the Gush which used to be the
  894. number of points to plot on this line is now an address to jump to in
  895. order to plot that number of points.
  896. .ID
  897. .I
  898. ycount = [gush++]
  899.  
  900. L1:
  901. scr += [gush++]
  902. jump to [gush++]
  903.  
  904. [...]
  905.  
  906. * ymxm = [gush++]
  907. * col = [ ymxm + rot ]
  908. * [scr] = col
  909. * scr += 1
  910.  
  911. ycount -= 1
  912. if ( ycount != 0 ) jump to L1
  913. .R
  914. .DE
  915. .PP
  916. The code marked by the asterisks plots one point. Assuming that
  917. \fIMAX_R\fR is the largest value of \fIRADIUS\fR ever likely to be used
  918. in the program, then the asterisked code needs to be repeated
  919. \fI2*MAX_R\fR times. The complete routine should take up no more than a
  920. few kilobytes.
  921. .NH 2
  922. The Compiled Gush
  923. .PP
  924. This is a way by which the animation can be dramatically increased
  925. in speed (and thanks to Jamie Lokier for pointing this out): instead of the
  926. Gush containing numerical data to be read by an animation routine, it
  927. can itself be an executable routine with the data hard-wired into it.
  928. .PP
  929. The Square Gush is translated directly into machine code, with a few
  930. bytes of code for each of the points to be plotted. A section of the
  931. Compiled Gush (working with an expanded Complexion) would then look
  932. something like:
  933. .ID
  934. .I
  935. col = [ m1 + rot ]
  936. [scr] = col
  937. scr += 1
  938.  
  939. col = [ m2 + rot ]
  940. [scr] = col
  941. scr += 1
  942.  
  943. col = [ m3 + rot ]
  944. [scr] = col
  945. scr += 1
  946.  
  947. [...]
  948. .R
  949. .DE
  950. .LP
  951. where \fIm1\fR, \fIm2\fR and \fIm3\fR are immediate (constant) values.
  952. .PP
  953. The Compiled Gush also contains machine instructions to
  954. move \fIscr\fR from the end of one line to the start of the next, and, of
  955. course, a 'return' instruction at the end. (Furthermore, quite a bit of
  956. optimization can often be done on the code as it is generated.) The planet
  957. is then animated simply by choosing values for \fIscr\fR and \fIrot\fR
  958. and calling the Compiled Gush code.
  959. .PP
  960. But there is one big drawback to this: a Compiled Gush takes up a
  961. lot of memory, several times more than a normal Gush. Fast animation is
  962. what this method delivers, but it does so at the cost of a large chunk
  963. of memory.
  964. .NH 1
  965. Finishing Off
  966. .NH 2
  967. Putting Together the Pieces
  968. .PP
  969. Way back at the beginning of all this I described the animation program
  970. as consisting of three basic parts. The code for creating Gush data
  971. is detailed in Section 2, the code for creating Complexion data is
  972. detailed in Section 3, and Section 5 constructs a routine that draws a
  973. planet using these two data arrays.
  974. .PP
  975. In Section 1.4, constant values (\fIrho\fR, \fIRADIUS\fR,
  976. \fIDIST\fR, etc.) are introduced. Whatever values these constants are
  977. given it is important that they remain the same in all parts of the
  978. program.
  979. .PP
  980. With all segments of the program complete an animation can
  981. finally be put together. The simplest animation, that of a planet spinning
  982. in the centre of the screen, is accomplished in the following way.
  983. .PP
  984. Create a Gush and a Complexion, storing both in files on disk. Write a
  985. simple piece of code that first loads in both data files and then, with an
  986. appropriate value for \fIscr\fR (the point on the screen where the
  987. planet is to appear), repeatedly calls the display routine. The
  988. additional parameter \fIrot\fR is increased (or decreased) by a
  989. small, fixed amount on each call with a modulus operation keeping it in the
  990. range \fI0\fR to \fI2*rho-1\fR.
  991. .PP
  992. There is not much more to say about the animation; further elaborations
  993. are left to the programmer's imagination. As a few suggestions:
  994. .IP  *
  995. Store several different Gushes on disk and select one to use at
  996. random when the animation is run. The Gushes are all created using
  997. different values for \fIphi\fR and \fItheta\fR (and possibly
  998. \fIRADIUS\fR).
  999. .IP  *
  1000. Use different sets of colours for different planets; choose the set
  1001. to be used at random when converting a Skin to a Complexion.
  1002. .IP  *
  1003. Make more interesting planet surfaces; add random cloud and mountain
  1004. generation, or edit a Complexion using a standard bitmap editor to give
  1005. it a smiling face.
  1006. .IP  *
  1007. Draw a star field behind the planet (to make the fact that it's supposed to
  1008. be a planet more obvious!). Animate the star field.
  1009. .IP  *
  1010. Shade the planet (as described in the next sub-section).
  1011. .IP  *
  1012. Before displaying the next frame of the planet, delete the old frame and
  1013. change the value of \fIscr\fR so that the planet appears in a different
  1014. place. Make the planet move. Make it bounce!
  1015. .NH 2
  1016. Illumination
  1017. .PP
  1018. In Section 2.3, when calculating values for the Gush a vector
  1019. \fIP=(xp,yp,zp)\fR is generated for each point \fI(xs,ys)\fR. This
  1020. vector can be used to give a basic illumination effect, that of
  1021. light shining onto the planet from a fixed direction.
  1022. .PP
  1023. For this effect to work, some method for selectively altering the
  1024. brightness of the pixels making up the planet's image is needed. Ideally,
  1025. each pixel should have a collection of bits controlling its brightness
  1026. which are independent of the bits controlling its colour. The planet can
  1027. then be shaded by adjusting just these brightness bits.
  1028. .PP
  1029. Define a vector \fII=(xi,yi,zi)\fR as the direction from which
  1030. light falls onto the planet. Then, for each \fI(xs,ys)\fR, calculate
  1031. a value \fIlambda\fR as follows:
  1032. .CD
  1033. .I
  1034. q = sqrt( xi*xi + yi*yi + zi*zi )
  1035.  
  1036. lambda =  ( I . P ) / ( |I| * |P| )
  1037. = ( xi*xp + yi*yp + zi*zp ) / ( q * g )
  1038. .R
  1039.  
  1040. (The calculation of the constant \fIg\fR is described in Section 2.3)
  1041. .DE
  1042. .PP
  1043. \fIlambda\fR is a measure of the illumination of the point \fI(xs,ys)\fR
  1044. and takes values from -1 (minimum brightness) to +1 (maximum brightness).
  1045. (Strictly, if \fIlambda\fR's value is below zero, then no light at all
  1046. reaches \fI(xs,ys)\fR - it is on the dark side of the planet.)
  1047. .PP
  1048. Transform each \fIlambda\fR into a number \fIkappa\fR, the
  1049. value to be put in the brightness bits of the pixel at (\fIxs,ys\fR).
  1050. To avoid the shading of the planet being too regular, add a slight
  1051. random influence to the transformation such as changing the value
  1052. of \fIlambda\fR by a small, random amount. This blurs the border
  1053. between 'light' and 'dark' regions of the planet.
  1054. .PP
  1055. Illuminating the planet is now a matter of keeping the brightness of
  1056. each pixel fixed (at the value \fIkappa\fR) whilst allowing the
  1057. planet display routine to fill in the pixel's colour.
  1058. .PP
  1059. One way of doing this would be to set each pixel to brightness
  1060. \fIkappa\fR and colour zero, and then use a logical OR operation in the
  1061. display routine to overlay colours onto the pixels. Before drawing the
  1062. next frame, the colour of each pixel would need to be reset to zero,
  1063. either by rewriting all brightness and colour values together, or by
  1064. resetting just the colour bits using multiple logical AND operations.
  1065. .NH 2
  1066. Whys and Wherefores
  1067. .PP
  1068. That's about it really. The one thing left to add is an
  1069. explanation. I've written some seven thousand words describing how to
  1070. put together a pretty but pointless animation. Why did I bother?
  1071. .PP
  1072. Well, as I said earlier, nearly all of the mildly clever animation
  1073. techniques used here I've had to work out for myself. They aren't the
  1074. sort of thing that you commonly find in text books. In a way this
  1075. document is a small attempt to change that - it's sort of a "Beginner's
  1076. Guide to Real-time Animation (Volume 1: Things That Bounce)".
  1077. .PP
  1078. Also, there's a limit to the number of things that I can think of
  1079. to do to spinning planets. I want to see what ideas other people come up
  1080. with using this as a starting point.
  1081. .PP
  1082. So, having read this document, if you're interested in bringing this
  1083. animation to life, go to it, and good luck to you.
  1084. .NH 2
  1085. Acknowledgements
  1086. .PP
  1087. Thanks must go to Matt Spinner for helping create the original planet
  1088. demo and to everyone else who was around at the time (Dave, Nick,
  1089. Simon, Phill, JT, etc).
  1090. .PP
  1091. And thanks also to Jay Dresser and Jamie Lokier from Usenet for comments
  1092. on the original posting.
  1093.